package com.wdciti.sccp.iot.springvertx.annotation;

import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.PropertyResolver;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * @author by early
 */
public class OnConfigCondition extends SpringBootCondition {

    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        List<AnnotationAttributes> allAnnotationAttributes = annotationAttributesFromMultiValueMap(
                Objects.requireNonNull(metadata.getAllAnnotationAttributes(ConditionalOnConfig.class.getName())));
        List<ConditionMessage> noMatch = new ArrayList<>();
        List<ConditionMessage> match = new ArrayList<>();
        for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
            ConditionOutcome outcome = determineOutcome(annotationAttributes, context.getEnvironment());
            (outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage());
        }
        if (!noMatch.isEmpty()) {
            return ConditionOutcome.noMatch(ConditionMessage.of(noMatch));
        }
        return ConditionOutcome.match(ConditionMessage.of(match));
    }

    private List<AnnotationAttributes> annotationAttributesFromMultiValueMap(
            MultiValueMap<String, Object> multiValueMap) {
        List<Map<String, Object>> maps = new ArrayList<>();
        multiValueMap.forEach((key, value) -> {
            for (int i = 0; i < value.size(); i++) {
                Map<String, Object> map;
                if (i < maps.size()) {
                    map = maps.get(i);
                } else {
                    map = new HashMap<>();
                    maps.add(map);
                }
                map.put(key, value.get(i));
            }
        });
        List<AnnotationAttributes> annotationAttributes = new ArrayList<>(maps.size());
        for (Map<String, Object> map : maps) {
            annotationAttributes.add(AnnotationAttributes.fromMap(map));
        }
        return annotationAttributes;
    }

    private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes, PropertyResolver resolver) {
        Spec spec = new Spec(annotationAttributes);
        boolean match = spec.matchProperties(resolver);
        return match ? ConditionOutcome.match() : ConditionOutcome.noMatch("no match properties");
    }

    private static class Spec {

        private final String name;

        private final String prefix;

        private final String[] keys;

        Spec(AnnotationAttributes annotationAttributes) {
            String prefix = annotationAttributes.getString("prefix").trim();
            if (StringUtils.hasText(prefix) && !prefix.endsWith(".")) {
                prefix = prefix + ".";
            }
            this.prefix = prefix;
            this.name = annotationAttributes.getString("name");
            this.keys = (String[]) annotationAttributes.get("keys");
        }

        private boolean matchProperties(PropertyResolver resolver) {
            for (String key : this.keys) {
                if (".enable".equals(key)) {
                    //先处理默认值${prefix}.${name}.enable
                    String prop = this.prefix + this.name + key;
                    return resolver.getProperty(prop, Boolean.class, true);
                } else {
                    //自定义的验证项想法太多不好办，就先验证name（这时配置文件的name就不能省略了）和true吧
                    String prop = this.prefix + key;
                    return resolver.getProperty(prop, "").contains(name)
                            || "true".equalsIgnoreCase(resolver.getProperty(prop));
                }
            }
            return false;
        }


        @Override
        public String toString() {
            return "Spec{" +
                    "name='" + name + '\'' +
                    ", prefix='" + prefix + '\'' +
                    ", keys=" + Arrays.toString(keys) +
                    '}';
        }
    }
}
